﻿using System;
using System.Collections.Specialized;
using System.Configuration;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using Vbyte.DataSource.Cache;
using Vbyte.Configuration;
using Vbyte.Extension;
using Vbyte.DataSource.Utility;

namespace Vbyte.DataSource.Data
{
    /// <summary>
    /// 远程数据加载辅助
    /// </summary>
    public static class DataExchangeHelper
    {
        /// <summary>
        /// 获取远程交互数据
        /// <para>异常1：BestTone.DataSource.ApiException数据交互异常，一般包含数据错误编号。</para>
        /// <para>异常2：System.NotSupportedException 远程数据格式不被支持</para>
        /// </summary>
        /// <param name="identity">该实例类型的标志</param>
        /// <returns>远程交互数据本地实例对象</returns>
        /// <exception cref="BestTone.DataSource.ApiException">BestTone.DataSource.ApiException 数据交互异常，一般包含数据错误编号。</exception>
        /// <exception cref="System.NotSupportedException">System.NotSupportedException</exception>
        public static T Load<T>(PropertyBindIdentity identity)
            where T : new()
        {
            Type instanceType = typeof(T);
            object[] objAttrs = instanceType.GetCustomAttributes(typeof(DataExchangeAttribute), true);
            if (objAttrs == null || objAttrs.Length < 1)
            {
                throw new ConfigurationErrorsException("实例类型配置错误，没有数据交互属性！");
            }
            else
            {
                DataExchangeAttribute exchangeConfig = (DataExchangeAttribute)objAttrs[0];
                string rawData = GetRemoteDataRaw(instanceType, exchangeConfig, identity);
                JSONParse jParse = new JSONParse(rawData);
                T instance = new T();
                //填充ID标志数据
                instance = instance.DataBind<T>(identity.GetObjectDictionary());
                //填充JSON数据
                jParse.FillObject<T>(instance, "");
                JsonExchange.Log("JSON-" + DateTime.Now.ToString("yyyy-MM-dd"), sw =>
                {
                    sw.WriteLine("完成数据实体绑定：{0}", instanceType.FullName);
                    sw.WriteLine();
                });
                return instance;
            }
        }

        /// <summary>
        /// 获取远程数据的原始数据
        /// </summary>
        /// <param name="instanceType">实例类型</param>
        /// <param name="exchangeConfig">交互数据获取配置</param>
        /// <param name="identity">该实例类型的标志</param>
        /// <returns>远程处理结果的原始文本</returns>
        private static string GetRemoteDataRaw(Type instanceType, DataExchangeAttribute exchangeConfig, PropertyBindIdentity identity)
        {
            string remoteURL = (identity.UseAlternateURIBind) ? exchangeConfig.AlternateURIBind : exchangeConfig.URIBind;
            if (exchangeConfig.DataFormat != RawFormat.JSON)
            {
                throw new NotSupportedException("目标数据目前只识别JSON数据的交互！");
            }

            string[] idArr = exchangeConfig.IdentityDepend;
            string[] idBind = new string[idArr.Length];
            string propName = string.Empty;
            for (int i = 0; i < idArr.Length; i++)
            {
                propName = idArr[i].Trim('{', '}');
                //属性绑定配置细化：{属性名} {属性方法()}
                idBind[i] = GetPropertyValue(instanceType, identity, propName);
            }

            string namePattern = "\\{(\\d+)\\}";
            if (Regex.IsMatch(remoteURL, namePattern, RegexOptions.IgnoreCase))
            {
                remoteURL = Regex.Replace(remoteURL, namePattern, m => idBind[int.Parse(m.Groups[1].Value)]);
            }
            else
            {
                //依据名称绑定
                namePattern = "\\{([a-zA-z_$][a-zA-z_0-9$]*)\\}";
                if (Regex.IsMatch(remoteURL, namePattern, RegexOptions.IgnoreCase))
                {
                    remoteURL = Regex.Replace(remoteURL, namePattern, m => GetPropertyValue(instanceType, identity, m.Groups[1].Value));
                }
            }

            string rawData = JsonExchange.GetApiJson(remoteURL, new NameValueCollection());
            if (JsonExchange.IsErrorCodeResult(rawData))
            {
                //细化各个模型的错误描述
                //throw new ApiException(ErrorCode.FromJSON<ErrorCode>(rawData));
            }
            return rawData;
        }

        private static string GetPropertyValue(Type instanceType, PropertyBindIdentity identity, string propName)
        {
            PropertyInfo pi = instanceType.GetProperty(propName);
            if (pi == null)
            {
                throw new ConfigurationErrorsException("远程数据交互的属性绑定配置错误，没有找到公开属性：[" + instanceType.FullName + "::" + propName + "] ！");
            }
            else
            {
                if (!pi.HasAttribute(typeof(QueryStringAbleAttribute), true))
                {
                    return identity.GetValue<string>(propName);
                }
                else
                {
                    return identity.GetValue<object>(propName).ToQueryString();
                }
            }
        }

        /// <summary>
        /// 抛出异常的封装(抛出或记录异常日志)
        /// </summary>
        /// <param name="instanceType">实例类型</param>
        /// <param name="currentExp">异常实例</param>
        /// <param name="logFileName">日志文件名</param>
        /// <param name="identity">相关数据标志</param>
        public static void ThrowException(Type instanceType, Exception currentExp, string logFileName, PropertyBindIdentity identity)
        {
            bool blnThrow = (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["DataSource.ThrowException"])
                && Convert.ToBoolean(ConfigurationManager.AppSettings["DataSource.ThrowException"]));
            if (blnThrow)
            {
                throw currentExp.GetTriggerException();
            }
            else
            {
                string key = "DataSource.LogToDB";
                bool blnUseDbLog = (!string.IsNullOrEmpty(ConfigurationManager.AppSettings[key])
                        && Convert.ToBoolean(ConfigurationManager.AppSettings[key]));

                if (!blnUseDbLog)
                {
                    //Gwsoft.SharpOrm.Util.Common.Log(logFileName, true, sw =>
                    //{
                    //    sw.WriteLine("获取数据异常：{0}", instanceType.FullName);
                    //    if (identity != null)
                    //    {
                    //        sw.WriteLine("标志参数：{0}", identity.ToString());
                    //    }
                    //    Exception targetExp = currentExp.GetTriggerException();
                    //    sw.WriteLine("错误消息：{0}", targetExp.Message);
                    //    sw.WriteLine("堆栈信息：{0}", targetExp.StackTrace);
                    //    sw.WriteLine();
                    //});
                }
                else
                {
                    StringBuilder sb = new StringBuilder();
                    if (identity != null)
                    {
                        sb.AppendFormat("标志参数：{0}\n", identity.ToString());
                    }
                    Exception targetExp = currentExp.GetTriggerException();
                    sb.AppendFormat("错误消息：{0}\n", targetExp.Message);
                    sb.AppendFormat("堆栈信息：{0}\n", targetExp.StackTrace);

                    //new SystemLog
                    //{
                    //    Category = "ERROR",Topic = "获取数据异常", LogTypeName = targetExp.GetType().FullName,
                    //    Message = sb.ToString()
                    //}.Insert();

                }
            }
        }

        /// <summary>
        /// MD5签名校验
        /// </summary>
        /// <param name="byteData">字节序列</param>
        /// <param name="IsLowerCase">是否小写输出</param>
        /// <returns>32Hash码</returns>
        public static string MD5(byte[] byteData, bool IsLowerCase)
        {
            if (byteData != null && byteData.LongLength > 0)
            {
                MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
                byte[] bytHash = md5.ComputeHash(byteData);
                md5.Clear();

                string sTemp = "";
                for (int i = 0; i < bytHash.Length; i++)
                {
                    sTemp += bytHash[i].ToString("X").PadLeft(2, '0');
                }
                return (IsLowerCase) ? sTemp.ToLower() : sTemp.ToUpper();
            }
            else
            {
                return string.Empty;
            }
        }

        /// <summary>
        /// 获取参数的序列码标志
        /// </summary>
        /// <param name="param">任意可序列化参数</param>
        /// <returns>32位MD5哈希码</returns>
        /// <exception cref="NotSupportedException">参数不支持二进制序列化</exception>
        /// <exception cref="ArgumentNullException">待获取标志的参数为null</exception>
        public static string HashParam(object param)
        {
            if (param == null) throw new ArgumentNullException("param");
            byte[] idDat = param.GetBytes();
            return MD5(idDat, true);
        }

    }
}
